﻿using Bystd.Geo.Converters;
using Bystd.Geo.Geometries;
using System;
using System.Collections.Generic;
using System.IO;

namespace Bystd.Geo.GeoParsers.EsirMdb
{
    /// <summary>
    /// geometry转换mdb,gdb转换字节码
    /// </summary>
    public class GeometryWriter
    {
        private BitExtensions bitExt = null;
        public GeometryWriter(ByteOrder byteOrder = ByteOrder.LittleEndian)
        {
            bitExt = new BitExtensions(byteOrder);
        }

        public virtual byte[] ToWriter(Geometry geometry)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                byte[] buf = null;
                int gtype = 0;

                if (geometry.GeometryType == GeoType.POINT)
                    gtype = 1;
                else if (geometry.GeometryType == GeoType.LINESTRING)
                    gtype = 3;
                else if (geometry.GeometryType == GeoType.POLYGON)
                    gtype = 5;
                else if (geometry.GeometryType == GeoType.MULTIPOINT)
                    gtype = 8;
                else
                    throw new Exception("geometryType not supported:" + geometry.GeometryType);

                //geometrytype
                buf = bitExt.GetBytes(gtype);
                ms.Write(buf, 0, buf.Length);

                if (geometry.GeometryType == GeoType.POINT)
                {
                    var p = geometry.GetGemoetry(0)[0][0];
                    buf = bitExt.GetBytes(p.X);
                    ms.Write(buf, 0, buf.Length);
                    buf = bitExt.GetBytes(p.Y);
                    ms.Write(buf, 0, buf.Length);
                    return ms.ToArray();
                }

                //min
                var box = geometry.GetBoundrayBox();
                buf = bitExt.GetBytes(box.Start.X);
                ms.Write(buf, 0, buf.Length);
                buf = bitExt.GetBytes(box.Start.Y);
                ms.Write(buf, 0, buf.Length);
                //max
                buf = bitExt.GetBytes(box.End.X);
                ms.Write(buf, 0, buf.Length);
                buf = bitExt.GetBytes(box.End.Y);
                ms.Write(buf, 0, buf.Length);


                //多点
                if (geometry.GeometryType == GeoType.MULTIPOINT)
                {
                    return multiPoint(geometry, ms);
                }

                //polygon和linestring
                var geo = geometry.GetGemoetry(0);

                return polygonlinestring(geo, ms);
            }
        }

        private byte[] multiPoint(Geometry geometry, MemoryStream ms)
        {
            var f_geo = geometry.GetGemoetry(0)[0];
            int count = f_geo.Count;
            var buf = bitExt.GetBytes(count);
            ms.Write(buf, 0, buf.Length);

            for (int i = 0; i < count; ++i)
            {
                buf = bitExt.GetBytes(f_geo[i].X);
                ms.Write(buf, 0, buf.Length);
                buf = bitExt.GetBytes(f_geo[i].Y);
                ms.Write(buf, 0, buf.Length);
            }
            return ms.ToArray();
        }

        private byte[] polygonlinestring(List<GeoSequence> geo, MemoryStream ms)
        {
            //polygon和linestring
            //var geo = geometry.GetGemoetry(0);
            long total_vertex = 0;

            //计算总顶点数
            for (int i = 0; i < geo.Count; ++i)
            {
                total_vertex += geo[i].Count;
            }

            var buf = bitExt.GetBytes(geo.Count);//总环数
            ms.Write(buf, 0, buf.Length);
            buf = bitExt.GetBytes(total_vertex);//总换的总顶点数
            ms.Write(buf, 0, buf.Length);

            //只有外环
            if (geo.Count <= 1)
            {
                var g = geo[0];
                for (int i = 0; i < g.Count; ++i)
                {
                    buf = bitExt.GetBytes(g[i].X);
                    ms.Write(buf, 0, buf.Length);
                    buf = bitExt.GetBytes(g[i].Y);
                    ms.Write(buf, 0, buf.Length);
                }
                return ms.ToArray();
            }

            //outer ring vertex number
            buf = bitExt.GetBytes(geo[0].Count);
            ms.Write(buf, 0, buf.Length);
            int num = geo[0].Count;

            //inner ring,第二个开始,最后一个环不计
            for (int i = 1; i < geo.Count - 1; ++i)
            {
                num += geo[i].Count;
                buf = bitExt.GetBytes(num);
                ms.Write(buf, 0, buf.Length);
            }

            //写入坐标点数据
            for (int i = 0; i < geo.Count; ++i)
            {
                var coords = geo[i];

                for (int j = 0; j < coords.Count; ++j)
                {
                    buf = bitExt.GetBytes(coords[i].X);
                    ms.Write(buf, 0, buf.Length);
                    buf = bitExt.GetBytes(coords[i].Y);
                    ms.Write(buf, 0, buf.Length);
                }
            }

            return ms.ToArray();
        }
    }
}
